﻿using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Reflection;
using System.Text;

namespace SuperX.Common.Helper
{
    /// <summary>
    /// 动态反射创建对象助手
    /// </summary>
    public static class DynamicEmitCalls
    {

        public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo)
        {
            FastInvokeHandler result;
            lock (dictInvoker)
            {
                if (DynamicEmitCalls.dictInvoker.ContainsKey(methodInfo))
                {
                    result = DynamicEmitCalls.dictInvoker[methodInfo];
                }
                else
                {
                    DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[]
                    {
                        typeof(object),
                        typeof(object[])
                    }, methodInfo.DeclaringType.Module);
                    ILGenerator ilgenerator = dynamicMethod.GetILGenerator();
                    ParameterInfo[] parameters = methodInfo.GetParameters();
                    Type[] array = new Type[parameters.Length];
                    for (int i = 0; i < array.Length; i++)
                    {
                        if (parameters[i].ParameterType.IsByRef)
                        {
                            array[i] = parameters[i].ParameterType.GetElementType();
                        }
                        else
                        {
                            array[i] = parameters[i].ParameterType;
                        }
                    }
                    LocalBuilder[] array2 = new LocalBuilder[array.Length];
                    for (int j = 0; j < array.Length; j++)
                    {
                        array2[j] = ilgenerator.DeclareLocal(array[j], true);
                    }
                    for (int k = 0; k < array.Length; k++)
                    {
                        ilgenerator.Emit(OpCodes.Ldarg_1);
                        DynamicEmitCalls.EmitFastInt(ilgenerator, k);
                        ilgenerator.Emit(OpCodes.Ldelem_Ref);
                        DynamicEmitCalls.EmitCastToReference(ilgenerator, array[k]);
                        ilgenerator.Emit(OpCodes.Stloc, array2[k]);
                    }
                    if (!methodInfo.IsStatic)
                    {
                        ilgenerator.Emit(OpCodes.Ldarg_0);
                    }
                    for (int l = 0; l < array.Length; l++)
                    {
                        ilgenerator.Emit(parameters[l].ParameterType.IsByRef ? OpCodes.Ldloca_S : OpCodes.Ldloc, array2[l]);
                    }
                    ilgenerator.EmitCall((!methodInfo.IsStatic) ? OpCodes.Callvirt : OpCodes.Call, methodInfo, null);
                    if (methodInfo.ReturnType == typeof(void))
                    {
                        ilgenerator.Emit(OpCodes.Ldnull);
                    }
                    else
                    {
                        DynamicEmitCalls.EmitBoxIfNeeded(ilgenerator, methodInfo.ReturnType);
                    }
                    for (int m = 0; m < array.Length; m++)
                    {
                        if (parameters[m].ParameterType.IsByRef)
                        {
                            ilgenerator.Emit(OpCodes.Ldarg_1);
                            DynamicEmitCalls.EmitFastInt(ilgenerator, m);
                            ilgenerator.Emit(OpCodes.Ldloc, array2[m]);
                            if (array2[m].LocalType.IsValueType)
                            {
                                ilgenerator.Emit(OpCodes.Box, array2[m].LocalType);
                            }
                            ilgenerator.Emit(OpCodes.Stelem_Ref);
                        }
                    }
                    ilgenerator.Emit(OpCodes.Ret);
                    FastInvokeHandler fastInvokeHandler = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
                    DynamicEmitCalls.dictInvoker.Add(methodInfo, fastInvokeHandler);
                    result = fastInvokeHandler;
                }
            }
            return result;
        }

        // 
        public static FastCreateInstanceHandler GetInstanceCreator(Type type)
        {
            Dictionary<Type, FastCreateInstanceHandler> obj = DynamicEmitCalls.dictCreator;
            FastCreateInstanceHandler result;
            lock (obj)
            {
                if (DynamicEmitCalls.dictCreator.ContainsKey(type))
                {
                    result = DynamicEmitCalls.dictCreator[type];
                }
                else
                {
                    DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, type, new Type[0], typeof(DynamicEmitCalls).Module);
                    ILGenerator ilgenerator = dynamicMethod.GetILGenerator();
                    ilgenerator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
                    ilgenerator.Emit(OpCodes.Ret);
                    FastCreateInstanceHandler fastCreateInstanceHandler = (FastCreateInstanceHandler)dynamicMethod.CreateDelegate(typeof(FastCreateInstanceHandler));
                    DynamicEmitCalls.dictCreator.Add(type, fastCreateInstanceHandler);
                    result = fastCreateInstanceHandler;
                }
            }
            return result;
        }

        // 
        public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo)
        {
            Dictionary<PropertyInfo, FastPropertyGetHandler> obj = DynamicEmitCalls.dictGetter;
            FastPropertyGetHandler result;
            lock (obj)
            {
                if (DynamicEmitCalls.dictGetter.ContainsKey(propInfo))
                {
                    result = DynamicEmitCalls.dictGetter[propInfo];
                }
                else
                {
                    DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[]
                    {
                        typeof(object)
                    }, propInfo.DeclaringType.Module);
                    ILGenerator ilgenerator = dynamicMethod.GetILGenerator();
                    ilgenerator.Emit(OpCodes.Ldarg_0);
                    ilgenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null);
                    DynamicEmitCalls.EmitBoxIfNeeded(ilgenerator, propInfo.PropertyType);
                    ilgenerator.Emit(OpCodes.Ret);
                    FastPropertyGetHandler fastPropertyGetHandler = (FastPropertyGetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler));
                    DynamicEmitCalls.dictGetter.Add(propInfo, fastPropertyGetHandler);
                    result = fastPropertyGetHandler;
                }
            }
            return result;
        }

        // 
        public static PropertyInfo GetPropertyInfo(Type type, string propertyName)
        {
            PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (propertyInfo == null && type.BaseType != typeof(object))
            {
                propertyInfo = DynamicEmitCalls.GetPropertyInfo(type.BaseType, propertyName);
            }
            return propertyInfo;
        }

        // 
        public static FieldInfo GetFieldInfo(Type type, string fieldName)
        {
            FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (fieldInfo == null && type.BaseType != typeof(object))
            {
                fieldInfo = DynamicEmitCalls.GetFieldInfo(type.BaseType, fieldName);
            }
            return fieldInfo;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propInfo"></param>
        /// <returns></returns>        
        public static FastPropertySetHandler GetPropertySetter(PropertyInfo propInfo)
        {
            Dictionary<PropertyInfo, FastPropertySetHandler> obj = DynamicEmitCalls.dictSetter;
            FastPropertySetHandler result;
            lock (obj)
            {
                if (DynamicEmitCalls.dictSetter.ContainsKey(propInfo))
                {
                    result = DynamicEmitCalls.dictSetter[propInfo];
                }
                else
                {
                    DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, null, new Type[]
                    {
                        typeof(object),
                        typeof(object)
                    }, propInfo.DeclaringType.Module);
                    ILGenerator ilgenerator = dynamicMethod.GetILGenerator();
                    ilgenerator.Emit(OpCodes.Ldarg_0);
                    ilgenerator.Emit(OpCodes.Ldarg_1);
                    DynamicEmitCalls.EmitCastToReference(ilgenerator, propInfo.PropertyType);
                    ilgenerator.EmitCall(OpCodes.Callvirt, propInfo.GetSetMethod(), null);
                    ilgenerator.Emit(OpCodes.Ret);
                    FastPropertySetHandler fastPropertySetHandler = (FastPropertySetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertySetHandler));
                    DynamicEmitCalls.dictSetter.Add(propInfo, fastPropertySetHandler);
                    result = fastPropertySetHandler;
                }
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ilGenerator"></param>
        /// <param name="type"></param>        
        private static void EmitCastToReference(ILGenerator ilGenerator, Type type)
        {
            if (type.IsValueType)
            {
                ilGenerator.Emit(OpCodes.Unbox_Any, type);
                return;
            }
            ilGenerator.Emit(OpCodes.Castclass, type);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ilGenerator"></param>
        /// <param name="type"></param>        
        private static void EmitBoxIfNeeded(ILGenerator ilGenerator, Type type)
        {
            if (type.IsValueType)
            {
                ilGenerator.Emit(OpCodes.Box, type);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ilGenerator"></param>
        /// <param name="value"></param>        
        private static void EmitFastInt(ILGenerator ilGenerator, int value)
        {
            switch (value)
            {
                case -1:
                    ilGenerator.Emit(OpCodes.Ldc_I4_M1);
                    return;
                case 0:
                    ilGenerator.Emit(OpCodes.Ldc_I4_0);
                    return;
                case 1:
                    ilGenerator.Emit(OpCodes.Ldc_I4_1);
                    return;
                case 2:
                    ilGenerator.Emit(OpCodes.Ldc_I4_2);
                    return;
                case 3:
                    ilGenerator.Emit(OpCodes.Ldc_I4_3);
                    return;
                case 4:
                    ilGenerator.Emit(OpCodes.Ldc_I4_4);
                    return;
                case 5:
                    ilGenerator.Emit(OpCodes.Ldc_I4_5);
                    return;
                case 6:
                    ilGenerator.Emit(OpCodes.Ldc_I4_6);
                    return;
                case 7:
                    ilGenerator.Emit(OpCodes.Ldc_I4_7);
                    return;
                case 8:
                    ilGenerator.Emit(OpCodes.Ldc_I4_8);
                    return;
                default:
                    if (value > -129 && value < 128)
                    {
                        ilGenerator.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
                        return;
                    }
                    ilGenerator.Emit(OpCodes.Ldc_I4, value);
                    return;
            }
        }

        /// <summary>
        /// 用于存放GetMethodInvoker的Dictionary
        /// </summary>
        private static Dictionary<MethodInfo, FastInvokeHandler> dictInvoker = new Dictionary<MethodInfo, FastInvokeHandler>();

        /// <summary>
        /// 用于存放GetInstanceCreator的Dictionary
        /// </summary>
        private static Dictionary<Type, FastCreateInstanceHandler> dictCreator = new Dictionary<Type, FastCreateInstanceHandler>();

        /// <summary>
        /// 用于存放GetPropertyGetter的Dictionary
        /// </summary>
        private static Dictionary<PropertyInfo, FastPropertyGetHandler> dictGetter = new Dictionary<PropertyInfo, FastPropertyGetHandler>();

        /// <summary>
        /// 用于存放SetPropertySetter的Dictionary
        /// </summary> 
        private static Dictionary<PropertyInfo, FastPropertySetHandler> dictSetter = new Dictionary<PropertyInfo, FastPropertySetHandler>();
    }
}
